import {warn} from '../util/warn'

export default {
    name: 'RouterView',
    functional: true,
    props: {
        name: {
            type: String,
            default: 'default'
        }
    },
    render(_, {props, children, parent, data}) {
        //标记组件为router-view
        data.routerView = true

        // directly use parent context's createElement() function
        // so that components rendered by router-view can resolve named slots
        const h = parent.$createElement
        const name = props.name
        const route = parent.$route
        const cache = parent._routerViewCache || (parent._routerViewCache = {})

        // determine current view depth, also check to see if the tree
        // has been toggled inactive but kept-alive.
        let depth = 0
        let inactive = false
        while (parent && parent._routerRoot !== parent) {
            if (parent.$vnode && parent.$vnode.data.routerView) {
                depth++
            }
            if (parent._inactive) {
                inactive = true
            }
            parent = parent.$parent
        }
        data.routerViewDepth = depth

        // render previous view if the tree is inactive and kept-alive
        if (inactive) {
            return h(cache[name], data, children)
        }

        //@todo 不知道这里是什么意思，为什么需要depth？通过depth获得了什么？
        const matched = route.matched[depth]
        // render empty node if no matched route
        if (!matched) {
            cache[name] = null
            return h()
        }

        const component = cache[name] = matched.components[name]

        // attach instance registration hook
        // this will be called in the instance's injected lifecycle hooks
        // 每一个router-view都有一个与之对应的vue实例，表示当前router-view所渲染的目标组件，
        // 而registerRouteInstance方法就是将router-view与vue实例对应起来
        data.registerRouteInstance = (vm, val) => {
            // val could be undefined for unregistration
            const current = matched.instances[name]
            if (
                (val && current !== vm) || //新增或者更新
                (!val && current === vm) //置空，也就是删除
            ) {
                //@todo 保存这个instances有什么作用呢？
                matched.instances[name] = val
            }
        }

        // also register instance in prepatch hook
        // in case the same component instance is reused across different routes
        ;(data.hook || (data.hook = {})).prepatch = (_, vnode) => {
            matched.instances[name] = vnode.componentInstance
        }

        // resolve props
        let propsToPass = data.props = resolveProps(route, matched.props && matched.props[name])
        if (propsToPass) {
            // clone to prevent mutation
            propsToPass = data.props = extend({}, propsToPass)
            // pass non-declared props as attrs
            const attrs = data.attrs = data.attrs || {}
            for (const key in propsToPass) {
                if (!component.props || !(key in component.props)) {
                    attrs[key] = propsToPass[key]
                    delete propsToPass[key]
                }
            }
        }

        return h(component, data, children)
    }
}

function resolveProps(route, config) {
    switch (typeof config) {
        case 'undefined':
            return
        case 'object':
            return config
        case 'function':
            return config(route)
        case 'boolean': //如果设置props属性为true，则会将params作为组件的props
            return config ? route.params : undefined
        default:
            if (process.env.NODE_ENV !== 'production') {
                warn(
                    false,
                    `props in "${route.path}" is a ${typeof config}, ` +
                    `expecting an object, function or boolean.`
                )
            }
    }
}

function extend(to, from) {
    for (const key in from) {
        to[key] = from[key]
    }
    return to
}
